package com.zboot.system.sharding.appender.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.zboot.comm.datasource.sharding.appender.ShardingDataNodeAppender;
import com.zboot.comm.datasource.sharding.service.ShardingTableRuleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.infra.datanode.DataNode;
import org.apache.shardingsphere.sharding.rule.TableRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class TestLogShardingNodeAppender implements ShardingDataNodeAppender {

    @Autowired
    private ShardingTableRuleService shardingTableRuleService;

    @Override
    public String getLogicTableName() {
        return "test_log";
    }

    /**
     * 是否需要追加，这里先100%需要追加
     * @param tableRule
     * @return
     */
    @Override
    public boolean needAppend(TableRule tableRule) {
        return true;
    }

    /**
     * 追加节点
     * @param tableRule
     */
    @Override
    public void append(TableRule tableRule) {
        //获取当前存在的表名
        String logicTableName = tableRule.getLogicTable();
        List<String> existsTableNames = shardingTableRuleService.getExistsTableNames(logicTableName);
        log.debug("---> 已存在{}个表：{}", existsTableNames.size(), existsTableNames);
        if(CollectionUtil.isEmpty(existsTableNames)) {
            throw new RuntimeException("请创建至少一个原型表");
        }

        //确定起始月份
        Date startMonth;
        String startMonthStr;
        if(existsTableNames.size()==1 && logicTableName.equals(existsTableNames.get(0))) {//仅有一个原型表，则从当月开始创建
            startMonth = new Date();
        }else {//否则取得最大月份，然后+1
            String maxTableName = existsTableNames.stream().max(Comparator.naturalOrder()).get();//获得名字最大的节点
            startMonthStr = maxTableName.replace(logicTableName + "_", "");//去掉真实表名和下划线，得到月份后缀
            startMonth = DateUtil.parse(startMonthStr, "yyyyMM");
            startMonth = DateUtil.offsetMonth(startMonth, 1);//向后移动一个月
        }
        startMonthStr = DateUtil.format(startMonth, "yyyyMM");

        //确定要生成的表
        List<DataNode> appendNodes = new ArrayList<>();//需要追加的节点
        int currentSize = existsTableNames.size();//已经存在的节点
        String dataSourceName = tableRule.getActualDataNodes().get(0).getDataSourceName();//数据源名称
        while(true) {
            if(appendNodes.size()>=appendSize() || currentSize+appendNodes.size()>maxSize()) {
                //如果添加数量达到了指定值，或者总量达到指定值则停止
                break;
            }
            appendNodes.add(new DataNode(dataSourceName+"."+logicTableName+"_"+startMonthStr));//添加到数据节点
            //月份向后移动一个月
            startMonth = DateUtil.offsetMonth(startMonth, 1);
            startMonthStr = DateUtil.format(startMonth, "yyyyMM");
        }
        if(CollectionUtil.isNotEmpty(appendNodes)) {//如果有表需要追加
            //先创建表
            Set<String> addTableNames = appendNodes.stream().map(DataNode::getTableName).collect(Collectors.toSet());
            log.debug("---> 需要创建{}个表", addTableNames.size());
            shardingTableRuleService.createTablesIfNotExists(addTableNames, logicTableName);
        }
        //无论是否有表添加，都要刷新共享配置
        List<DataNode> afterDataNodes = existsTableNames.stream()
                .map(e -> new DataNode(dataSourceName + "." + e)).collect(Collectors.toList());
        afterDataNodes.addAll(appendNodes);
        log.debug("---> 刷新 {} 完成，现在有{}个节点", tableRule.getLogicTable(), afterDataNodes.size());
        shardingTableRuleService.freshActualDataNodes(dataSourceName, tableRule, afterDataNodes);
    }
}
